package com.buka.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.buka.common.BizCodeEnum;
import com.buka.constants.Constants;
import com.buka.dao.CouponDao;
import com.buka.dao.CouponRecordDao;
import com.buka.entity.Coupon;
import com.buka.entity.CouponRecord;
import com.buka.exception.BizException;
import com.buka.interceptor.LoginUserInterceptor;
import com.buka.request.NewUserCouponRequest;
import com.buka.service.CouponService;
import com.buka.util.CommonUtil;
import com.buka.util.JsonData;
import com.buka.vo.CouponVo;
import com.buka.vo.LoginUserVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * (Coupon)表服务实现类
 *
 * @author makejava
 * @since 2025-08-06 14:58:24
 */
@Service("couponService")
public class CouponServiceImpl extends ServiceImpl<CouponDao, Coupon> implements CouponService {

    @Resource
    CouponRecordDao couponRecordDao;
    @Resource
    RedisTemplate redisTemplate;
    @Resource
    RedissonClient redissonClient;

    @Override
    public JsonData pageCoupon(Page page, CouponVo couponVo) {

        LambdaQueryWrapper<Coupon> queryWrapper = Wrappers.lambdaQuery(Coupon.class);
        //查询已发布的优惠券
        queryWrapper.eq(Coupon::getPublish, Constants.CouponStateEnum.PUBLISH.name());
        String category = couponVo.getCategory();
        queryWrapper.eq(StrUtil.isNotBlank(category), Coupon::getCategory, category);
        queryWrapper.orderByDesc(Coupon::getCreateTime);
        Page<CouponVo> result = page(page, queryWrapper);

        return JsonData.buildSuccess(result);
    }

    @Transactional
    @Override
    public JsonData claimCoupon(Long couponId) throws InterruptedException {
        Long id = LoginUserInterceptor.threadLocal.get().getId();
        //当前写法不具备原子性

        //优惠券的id 是1  用户是张三
        String lockKey = "coupon:claim:lock" + couponId;//某个用户某类优惠券领取限额

        String val = CommonUtil.getRandomCode(20);
        //原子操作
        Boolean aBoolean;
        //自旋等待
        if (!(aBoolean = redisTemplate.opsForValue().setIfAbsent(lockKey, val, Duration.ofSeconds(30)))) {  //说明已经加锁了,自旋等待
            TimeUnit.MILLISECONDS.sleep(100);
            claimCoupon(couponId);

        }


        try {
            addCoupon(couponId, id);
        } catch (Exception e) {
            e.printStackTrace();
        }


        //释放锁 需要判断 值是不是该锁的持有者
        String unlockScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long aLong = (Long) redisTemplate.execute(new DefaultRedisScript(unlockScript, Long.class),
                Arrays.asList(new String[]{lockKey}),
                val);


        return JsonData.buildSuccess();
    }


    //领券
    @Transactional
    public JsonData claimCouponByRedisson(Long couponId) throws InterruptedException {
        Long id = LoginUserInterceptor.threadLocal.get().getId();
        //当前写法不具备原子性

        //优惠券的id 是1  用户是张三
        String lockKey = "coupon:claim:lock" + couponId;//某个用户某类优惠券领取限额

        String val = CommonUtil.getRandomCode(20);
        //原子操作

        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(30,TimeUnit.SECONDS);

        try {
            addCoupon(couponId, id);
        } catch (Exception e) {
            throw e;
        }finally {
            lock.unlock();
        }
        return JsonData.buildSuccess();
    }

    private void addCoupon(Long couponId, Long id) {


        Coupon coupon = baseMapper.selectById(couponId);

        //校验优惠券领取权限
        checkCoupon(coupon);

        //修改库存
//        Integer stock = coupon.getStock();
//        coupon.setStock(stock - 1);
//        baseMapper.updateById(coupon);


        //插入一条领取记录
        CouponRecord couponRecord = new CouponRecord();
        BeanUtils.copyProperties(coupon, couponRecord);
        couponRecord.setCouponId(couponId);
        couponRecord.setCreateTime(new Date());
        couponRecord.setUseState(Constants.CoupUseStateEnum.NEW.name());


        couponRecord.setUserId(id);
        couponRecord.setId(null);//因为是复制的优惠券信息，id 也复制过来了，需要清除id值，由mybatis生成id

        couponRecordDao.insert(couponRecord);
    }

    private int reduceStock(Long couponId) {
        return baseMapper.reduceStock(couponId);
    }

    //校验优惠券领取权限，无法领取直接抛出异常
    private void checkCoupon(Coupon coupon) {
        //首先判断优惠券是否为空
        if (null == coupon) {
            throw new BizException(BizCodeEnum.COUPON_NOT_EXIST);
        }

        //判断是否为发布状态
        if (!coupon.getPublish().equals(Constants.CouponStateEnum.PUBLISH.name())) {
            throw new BizException(BizCodeEnum.COUPON_GET_FAIL);
        }

        //是否在领取时间范围内
        Date startTime = coupon.getStartTime();
        Date endTime = coupon.getEndTime();
        Date current = new Date();
        if (startTime.after(current) || endTime.before(current)) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_TIME);
        }

        //用户是否超领  查询当前用户领取的数量是否在于优惠券领取限制
        LambdaQueryWrapper<CouponRecord> queryWrapper = Wrappers.lambdaQuery(CouponRecord.class);
        queryWrapper.eq(CouponRecord::getUserId, LoginUserInterceptor.threadLocal.get().getId());
        queryWrapper.eq(CouponRecord::getCouponId, coupon.getId());
        int count = couponRecordDao.selectCount(queryWrapper);
        if (count >= coupon.getUserLimit()) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_LIMIT);
        }

        //库存是否充足
        if (reduceStock(coupon.getId()) == 0) {
            throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }
    }

    //新用户注册 成功，直接领取新人券
    @Override
    public JsonData getNewUserCoupon(NewUserCouponRequest user){

        LambdaQueryWrapper<Coupon> queryWrapper = Wrappers.lambdaQuery(Coupon.class);
        queryWrapper.eq(Coupon::getCategory, Constants.CouponCategoryEnum.NEW_USER.name())
                .eq(Coupon::getPublish, Constants.CouponStateEnum.PUBLISH.name());
        List<Coupon> list = list(queryWrapper);

        LoginUserVo loginUserVo = new LoginUserVo();
        loginUserVo.setId(user.getUserId());
        LoginUserInterceptor.threadLocal.set(loginUserVo);

        //调用领券方法
        for (Coupon coupon : list) {
            addCoupon(coupon.getId(),user.getUserId());
        }

        return JsonData.buildSuccess();

    }
}

